package internal

import (
	"context"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"rsync-code/common"
	"strings"
	"sync"
	"time"

	"github.com/spf13/viper"
)

// 已移除重试机制

// ProgressReporter 用于报告进度
type ProgressReporter struct {
	mu       sync.Mutex
	progress map[string]float64
}

// NewProgressReporter 创建新的进度报告器
func NewProgressReporter() *ProgressReporter {
	return &ProgressReporter{
		progress: make(map[string]float64),
	}
}

// UpdateProgress 更新进度
func (p *ProgressReporter) UpdateProgress(operation string, progress float64) {
	p.mu.Lock()
	defer p.mu.Unlock()
	p.progress[operation] = progress
	common.Log.Infof("进度报告 - %s: %.1f%%", operation, progress)
}

// GetProgress 获取进度
func (p *ProgressReporter) GetProgress(operation string) float64 {
	p.mu.Lock()
	defer p.mu.Unlock()
	return p.progress[operation]
}

// executeGitCommandWithRetry 带重试机制的 git 命令执行
func executeGitCommandWithRetry(ctx context.Context, localPath string, reporter *ProgressReporter, operation string, args ...string) error {
	// 创建带上下文的命令
	cmd := exec.CommandContext(ctx, "git", args...)

	// 设置环境变量
	cmd.Env = append(os.Environ(),
		"GIT_TERMINAL_PROMPT=0",
		"GIT_SSH_COMMAND=ssh -o StrictHostKeyChecking=no",
		"GIT_DISCOVERY_ACROSS_FILESYSTEM=1",
	)

	// 设置工作目录
	cmd.Dir = localPath

	// 创建管道来获取实时输出
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return fmt.Errorf("创建标准输出管道失败: %v", err)
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return fmt.Errorf("创建标准错误管道失败: %v", err)
	}

	// 启动命令
	if err := cmd.Start(); err != nil {
		return fmt.Errorf("启动命令失败: %v", err)
	}

	// 使用 WaitGroup 确保输出读取完成
	var wg sync.WaitGroup
	wg.Add(2)

	// 读取标准输出
	go func() {
		defer wg.Done()
		io.Copy(os.Stdout, stdout)
	}()

	// 读取标准错误
	go func() {
		defer wg.Done()
		io.Copy(os.Stderr, stderr)
	}()

	// 等待命令完成
	err = cmd.Wait()

	// 等待输出读取完成
	wg.Wait()

	if err == nil {
		reporter.UpdateProgress(operation, 100)
		common.Log.Infof("命令执行成功: git %s", strings.Join(args, " "))
		return nil
	}

	return fmt.Errorf("执行 %s 失败: %v", operation, err)
}

// getRepositoryURLs 获取仓库相关的 URL
func getRepositoryURLs(owner, repo string) (gitcodeURL, gitcodeHttpsURL, sourceURL string) {
	// 获取源仓库类型，默认为 gitee
	sourceType := viper.GetString("source_repository_type")
	if sourceType == "" {
		sourceType = "gitee"
	}

	// 根据源仓库类型构建源 URL
	switch sourceType {
	case "gitcode.net":
		// 获取 gitcode.net 认证信息
		gitcodeNetToken := viper.GetString(fmt.Sprintf("gitcode_net_token_%s", owner))
		gitcodeNetUsername := viper.GetString(fmt.Sprintf("gitcode_net_username_%s", owner))

		if gitcodeNetToken != "" && gitcodeNetUsername != "" {
			sourceURL = fmt.Sprintf("https://%s:%s@gitcode.net/%s/%s.git",
				gitcodeNetUsername, gitcodeNetToken, owner, repo)
		} else {
			// 如果没有配置认证信息，使用 SSH 方式
			sourceURL = fmt.Sprintf("git@gitcode.net:%s/%s.git", owner, repo)
		}
	default: // gitee
		// 获取 gitee 认证信息
		giteeToken := viper.GetString(fmt.Sprintf("gitee_token_%s", owner))
		giteeUsername := viper.GetString(fmt.Sprintf("gitee_username_%s", owner))

		sourceURL = fmt.Sprintf("https://%s:%s@gitee.com/%s/%s.git",
			giteeUsername, giteeToken, owner, repo)
	}

	// 构建项目标识
	projectKey := fmt.Sprintf("%s/%s", owner, repo)

	// 检查是否需要映射到其他组织
	targetOrg := owner
	for _, mapping := range viper.GetStringSlice("project_to_org") {
		parts := strings.Split(mapping, ":")
		if len(parts) == 2 && parts[0] == projectKey {
			targetOrg = parts[1]
			break
		}
	}

	// 构建最终的 URLs
	gitcodeURL = fmt.Sprintf("git@gitcode.com:%s/%s.git", targetOrg, repo)
	gitcodeHttpsURL = fmt.Sprintf("https://gitcode.com/%s/%s.git", targetOrg, repo)

	return
}

// cloneRepository 克隆仓库
func cloneRepository(ctx context.Context, sourceURL, localPath string, reporter *ProgressReporter) error {
	common.Log.Infof("开始克隆仓库: %s", strings.Replace(sourceURL, "https://", "https://***", 1))

	// 确保父目录存在
	parentDir := filepath.Dir(localPath)
	if err := os.MkdirAll(parentDir, 0755); err != nil {
		return fmt.Errorf("创建父目录失败 %s: %v", parentDir, err)
	}

	// 检查目标目录是否已存在
	updated := false
	if _, err := os.Stat(localPath); err == nil {
		if viper.GetBool("update_if_exists") {
			common.Log.Infof("目标目录已存在，执行更新操作: %s", localPath)
			if err := executeGitCommandWithRetry(ctx, localPath, reporter, "更新仓库",
				"fetch", "--all", "--prune"); err != nil {
				return fmt.Errorf("更新仓库失败: %v", err)
			}
			updated = true
		} else {
			common.Log.Warnf("目标目录已存在，删除后重新克隆: %s", localPath)
			if err := os.RemoveAll(localPath); err != nil {
				return fmt.Errorf("删除已存在的目录失败 %s: %v", localPath, err)
			}
		}
	}

	// 若未更新（不存在或选择删除重新拉取），则执行克隆
	if !updated {
		common.Log.Infof("开始执行git clone命令，目标目录: %s", localPath)
		if err := executeGitCommandWithRetry(ctx, parentDir, reporter, "克隆仓库",
			"clone", "--mirror", sourceURL, filepath.Base(localPath)); err != nil {
			return fmt.Errorf("克隆仓库失败: %v", err)
		}
		reporter.UpdateProgress("克隆仓库", 50)
	}

	// 验证克隆是否成功
	if _, err := os.Stat(localPath); os.IsNotExist(err) {
		return fmt.Errorf("克隆仓库失败：目标目录不存在")
	}

	// 检查是否是有效的git仓库
	if err := executeGitCommandWithRetry(ctx, localPath, reporter, "验证仓库",
		"rev-parse", "--git-dir"); err != nil {
		return fmt.Errorf("克隆仓库失败：目标目录不是有效的Git仓库")
	}

	// 检查是否是裸仓库
	if err := executeGitCommandWithRetry(ctx, localPath, reporter, "验证裸仓库",
		"rev-parse", "--is-bare-repository"); err != nil {
		return fmt.Errorf("克隆仓库失败：目标目录不是有效的裸仓库")
	}

	// 检查是否启用 LFS 功能
	if viper.GetBool("enable_lfs") {
		// 获取 LFS 文件
		common.Log.Infof("开始获取 LFS 文件")
		if err := executeGitCommandWithRetry(ctx, localPath, reporter, "获取 LFS 文件",
			"lfs", "fetch", "--all", "--prune"); err != nil {
			common.Log.Warnf("获取 LFS 文件失败: %v", err)
			// LFS 失败不影响整体流程，继续执行
		}
		reporter.UpdateProgress("获取 LFS 文件", 100)
	} else {
		common.Log.Infof("LFS 功能已禁用，跳过获取 LFS 文件")
	}

	return nil
}

// pushToGitCode 推送代码到 GitCode
func pushToGitCode(ctx context.Context, localPath, gitcodeHttpsURL, gitcodeURL string, reporter *ProgressReporter) error {
	// 检查是否启用 LFS 功能
	if viper.GetBool("enable_lfs") {
		// 推送 LFS 文件前先获取所有 LFS 文件
		common.Log.Infof("推送前获取所有 LFS 文件")
		if err := executeGitCommandWithRetry(ctx, localPath, reporter, "获取 LFS 文件",
			"lfs", "fetch", "--all"); err != nil {
			common.Log.Warnf("获取 LFS 文件失败: %v", err)
			// LFS 获取失败不影响整体流程，继续执行
		}
		reporter.UpdateProgress("获取 LFS 文件", 25)

		// 推送 LFS 文件
		common.Log.Infof("开始推送 LFS 文件")
		if err := executeGitCommandWithRetry(ctx, localPath, reporter, "推送 LFS 文件",
			"lfs", "push", gitcodeHttpsURL, "--all"); err != nil {
			return err
		}
		reporter.UpdateProgress("推送 LFS 文件", 50)
	} else {
		common.Log.Infof("LFS 功能已禁用，跳过 LFS 相关操作")
	}

	// 推送代码
	common.Log.Infof("开始推送代码")
	if err := executeGitCommandWithRetry(ctx, localPath, reporter, "推送代码",
		"push", "--mirror", "-f", gitcodeURL); err != nil {
		return err
	}
	reporter.UpdateProgress("推送代码", 100)

	return nil
}

// GitPushSSH 将仓库从 Gitee 推送到 GitCode
func GitPushSSH(fullName string) error {
	// 创建上下文，设置超时时间
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute)
	defer cancel()

	// 创建进度报告器
	reporter := NewProgressReporter()

	codePath := viper.GetString("code_path")
	owner, repo := GetOwnerRepo([]string{fullName})

	// 获取仓库 URL
	gitcodeURL, gitcodeHttpsURL, sourceURL := getRepositoryURLs(owner, repo)

	// 创建组织目录
	orgPath := filepath.Join(codePath, owner)
	if err := os.MkdirAll(orgPath, 0755); err != nil {
		return fmt.Errorf("创建组织目录失败: %v", err)
	}

	// 设置本地仓库路径（不包含.git后缀）
	localPath := filepath.Join(orgPath, repo)
	gitPath := localPath + ".git"

	common.Log.Infof("仓库路径信息：")
	common.Log.Infof("组织目录: %s", orgPath)
	common.Log.Infof("本地仓库路径: %s", localPath)
	common.Log.Infof("Git仓库路径: %s", gitPath)
	common.Log.Infof("源仓库 URL: %s", strings.Replace(sourceURL, "https://", "https://***", 1))

	// 是否存在由 cloneRepository 内部自行处理（根据 update_if_exists 配置决定更新或删除重拉）

	// 克隆仓库
	if err := cloneRepository(ctx, sourceURL, gitPath, reporter); err != nil {
		return err
	}

	// 测试环境使用配置的组织名
	if !viper.GetBool("is_prod") {
		gitcodeURL = fmt.Sprintf("git@gitcode.com:%s/%s.git", viper.GetString("gitcode_org"), repo)
		gitcodeHttpsURL = fmt.Sprintf("https://gitcode.com/%s/%s.git", viper.GetString("gitcode_org"), repo)
	}

	// 推送代码到 GitCode
	if err := pushToGitCode(ctx, gitPath, gitcodeHttpsURL, gitcodeURL, reporter); err != nil {
		return err
	}

	common.Log.Infof("项目【%s】同步完成", fullName)
	return nil
}
